/*
   Copyright (c) 2023 李伟国
   learner-platform is licensed under Mulan PSL v2.
   You can use this software according to the terms and conditions of the Mulan PSL v2.
   You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2
   THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
   See the Mulan PSL v2 for more details.
*/

package cn.platform.service.impl;

import cn.platform.config.JudgeSourceProperties;
import cn.platform.constant.ProblemConstant;
import cn.platform.dto.CodeRunMsg;
import cn.platform.dto.CodeRunResult;
import cn.platform.dto.TestInOut;
import cn.platform.entity.Runner;
import cn.platform.service.IJudgeService;
import cn.platform.utils.FileUtil;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class JudgeServiceImpl implements IJudgeService, ProblemConstant {

    private static final Logger logger = LoggerFactory.getLogger(JudgeServiceImpl.class);

    /**
     * 语言的配置路径
     */
    private static String languageConfigPath;

    /**
     * acm-online-judge模块所在的地址
     */
    private static String onlineJudgePath;

    /**
     * 题目的根路径
     */
    private static String problemRootPath;

    /**
     * 保存测试用例
     *
     * @param problemId  题目id
     * @param testInOuts 输入输出列表
     * @return 是否保存成功
     */
    @Override
    public boolean saveTestCase(Integer problemId, List<TestInOut> testInOuts) {
        return saveTest(TEST_TYPE_CASE, problemId, testInOuts);
    }

    /**
     * 保存题目中已经展示的测试样例
     *
     * @param problemId  题目id
     * @param testInOuts 输入输出列表
     * @return 是否保存成功
     */
    @Override
    public boolean saveTestSample(Integer problemId, List<TestInOut> testInOuts) {
        return saveTest(TEST_TYPE_SAMPLE, problemId, testInOuts);
    }

    /**
     * 按照类型保存测试数据
     *
     * @param type       保存的类型 CASE：测试用例（未展示在题目中的）  SAMPLES：测试样例（展示在题目中的）
     * @param problemId  题目id
     * @param testInOuts 输入输出列表
     * @return 是否保存成功
     */
    private boolean saveTest(String type, Integer problemId, List<TestInOut> testInOuts) {
        String problemPath = String.format("%s%s%d", problemRootPath, File.separator, problemId);
        String test = String.format("%s%s%s", problemPath, File.separator, type);
        File testDir = new File(test);
        if (!testDir.exists()) {
            boolean mkdirs = testDir.mkdirs();
            if (!mkdirs) {
                logger.error("文件夹创建失败");
                return false;
            }
        } else {
            // 删除test下的所有文件
            FileUtil.deleteFilesInFolder(test);
        }
        if (testInOuts == null) {
            logger.warn("没有给出测试数据");
            return false;
        }
        boolean result = true;
        // 创建测试数据
        int rank = 0;
        for (TestInOut testInOut : testInOuts) {
            result &= FileUtil.writeFile(String.format("%s%s%d.in", test, File.separator, testInOut.getRank()== null? rank:testInOut.getRank()) , testInOut.getInput());
            result &= FileUtil.writeFile(String.format("%s%s%d.out", test, File.separator, testInOut.getRank()== null? rank:testInOut.getRank()), testInOut.getOutput());
            rank++;
        }
        return result;
    }

    /**
     * 向本地创建测试代码
     *
     * @param codeRunMsg 测试代码
     * @return 测试代码文件的路径
     */
    @Override
    public String saveTestCode(CodeRunMsg codeRunMsg) {
        String problemPath = String.format("%s%s%d", problemRootPath, File.separator, codeRunMsg.getProblemId());
        String problemTestCodeFolder = problemPath + File.separator + TEST_CODE_FOLDER_NAME;
        String problemTestCode = String.format("%s%s%s%s", problemTestCodeFolder, File.separator, codeRunMsg.getFilePrefix(), codeRunMsg.getFileSuffix());
        boolean b = FileUtil.writeFile(problemTestCode, codeRunMsg.getCode());
        return problemTestCode;
    }

    /**
     * 运行测试代码
     *
     * @param runner 测试代码信息
     * @return 运行结果
     */
    @Override
    public CodeRunResult runTestCode(Runner runner) {
        String s = saveTestCode(runner.getCodeRunMsg());
        runner.setFilePath(s)
                .setCodeResultDir(new File(s).getParent());
        return this.runCode(runner);
    }

    /**
     * 运行用户的代码
     *
     * @param userId 用户id
     * @param runner 用户的代码信息
     * @return 运行结果
     */
    @Override
    public CodeRunResult runUserCode(Long userId, Runner runner) {
        String codeFilePath = saveUserCode(userId, runner.getCodeRunMsg());
        runner.setFilePath(codeFilePath)
                .setCodeResultDir(new File(codeFilePath).getParent());
        return this.runCode(runner);
    }

    private String saveUserCode(Long userId, CodeRunMsg codeRunMsg) {
        String problemPath = String.format("%s%s%d", problemRootPath, File.separator, codeRunMsg.getProblemId());
        String userPath = String.format("%s%s%s%s%d", problemPath, File.separator, RUN_USER_FOLDER_NAME, File.separator, userId);
        String codePath = String.format("%s%s%s", userPath, File.separator, UUID.randomUUID());
        String codeFile = String.format("%s%s%s%s", codePath, File.separator, codeRunMsg.getFilePrefix(), codeRunMsg.getFileSuffix());
        FileUtil.writeFile(codeFile, codeRunMsg.getCode());
        return codeFile;
    }

    /**
     * 判题机路径
     */
    @Deprecated
    private static String onlineJudge;

    /**
     * 语言目录
     */
    @Deprecated
    private static String langConfigPath;

    /**
     * 测试样例的父级文件夹
     */
    @Deprecated
    private static String testDataParentDir;

    /**
     * 代码结果生成的根文件夹
     */
    @Deprecated
    private static String codeResultRootDir;


    @Autowired
    private JudgeSourceProperties judgeSource(JudgeSourceProperties judgeSourcePropertiess) {
        onlineJudge = judgeSourcePropertiess.getOnlineJudge();
        testDataParentDir = judgeSourcePropertiess.getTestData();
        codeResultRootDir = judgeSourcePropertiess.getCodeResultRootPath();
        langConfigPath = judgeSourcePropertiess.getLanguageConfig();

        onlineJudgePath = judgeSourcePropertiess.getOnlineJudgePath();
        languageConfigPath = judgeSourcePropertiess.getLanguageConfigPath();
        problemRootPath = judgeSourcePropertiess.getProblemRootPath();
        return judgeSourcePropertiess;
    }

    @Override
    public String saveCode(String code, String filePrefix, String fileSuffix) {
        String dir = UUID.randomUUID().toString().replaceAll("-", "");
        File file = new File(codeResultRootDir + File.separator + dir + File.separator + filePrefix + fileSuffix);
        boolean mkdirs = file.getParentFile().mkdirs();
        try (FileWriter fileWriter = new FileWriter(file, false)) {
            fileWriter.write(code);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(file.getParent());
        return file.getParent();
    }

    /**
     * 保存题目的测试样例，将题目的测试样例存在本地的某个文件夹中
     *
     * @return 操作是否成功
     */
    @Override
    public boolean saveTestCase() {
        // TODO 保存测试样例， 0.in 0.out
        System.out.println(onlineJudge);
        System.out.println(langConfigPath);
        System.out.println(codeResultRootDir);
        System.out.println(testDataParentDir);
        return false;
    }

    @Override
    public CodeRunResult runCode(Runner runner) {
        List<Object> resultList = new ArrayList<>();
        ProcessBuilder builder = new ProcessBuilder(runner.getRunCmd().split(" "));
        builder.redirectErrorStream(true);
        try {
            Process process = builder.start();
            try (InputStream inputStream = process.getInputStream();
                 BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
                Object lines;
                while ((lines = br.readLine()) != null) {
                    resultList.add(lines);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        int size = resultList.size();
        CodeRunResult codeRunResult = new CodeRunResult();
        System.out.println(resultList.toString());
        codeRunResult.setRunResult(JSON.parse(String.valueOf(resultList.get(size - 1))));
        codeRunResult.setCompileResult(
                resultList.stream()
                        .limit(size - 1)
                        .collect(Collectors.toList()));
        return codeRunResult;
    }
}
